#ifndef RULE_H
#define RULE_H

#include "core/expression.h"

typedef QPair<int,int> Position;
class Condition;

class NCA_EXPORT Rule
  {
  friend class RuleTest;
public:
  Rule(const Expression& pattern, const Expression& replacement);

  const Expression & key() const { return _pattern; }
  const Expression & value() const { return _replacement; }

  void addCondition(Condition condition);
  void addConditions(QList<Condition> conditions);
  // @returns Position where pattern starts in exp
  Position findPatternInExp(Expression const & exp) const;
  bool apply(Expression & exp, Position pos) const;

  // Helper function for Rule::apply but is also used in Configuration.
  QMap<QString, QStringList> getLabelDictionary(Expression const &other) const;

protected:
  Expression _pattern, _replacement;
  // we're storing objects, because they are small and the list won't change often.
  QList<Condition> _conditions;

  /*
  @args exp Expression containing part that matches the pattern.
  @returns true if the part meets other criteria besides matching the pattern.
  */
  bool checkForDependencies(const Expression &exp,
                            int startingSentence,
                            int startingWord) const;
  bool checkCondition(const Expression &exp,
                      int startingSentence,
                      int startingWord, 
                      Condition const &condition) const;
  QPair<int, int> computePositionInExp(int startingSentence,
                                       int startingWord,
                                       Position positionInPattern) const;
  };

class Condition
  {
  public:
    enum ConditionType
      {
      CT_EQUALS = 0,
      CT_GREATER,
      CT_GREATER_OR_EQUAL,
      CT_NOT_EQUAL,
      CT_INVALID
      };
    Condition() :
      type(CT_INVALID), arguments(QStringList()) { }
    Condition(ConditionType aType, QStringList args) :
      type(aType), arguments(args) { }

    // those should be const, but aren't, because we are
    // storing this in qlist which requires operator=
    ConditionType type;
    QStringList arguments;
  };

Q_DECLARE_METATYPE(Condition)

inline bool operator<(const QStringList &e1, const QStringList &e2)
  {
  if (e1.size() != e2.size())
    return e1.size() < e2.size();
  for (int i = 0; i < e1.size(); i++)
    for (int j = 0; j < e2.size(); j++)
      if (e1.at(i) != e2.at(j))
        return (e1.at(i) < e2.at(j));
  return false;
  }

#endif // RULE_H
